package clock.socoolby.com.clock.widget.textview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.util.AttributeSet;

import androidx.annotation.Nullable;

import com.xenione.digit.TabConfig;

import java.util.HashMap;
import java.util.Random;

import clock.socoolby.com.clock.widget.textview.charanimator.AbstractCharAnimator;
import clock.socoolby.com.clock.widget.textview.charanimator.CharAnimatorEnum;
import clock.socoolby.com.clock.widget.textview.charanimator.Down2UpCharAnimator;
import clock.socoolby.com.clock.widget.textview.charanimator.Marquee3DCharAnimator;
import clock.socoolby.com.clock.widget.textview.charanimator.TabDigitCharAnimator;
import clock.socoolby.com.clock.widget.textview.charanimator.Up2DownCharAnimator;

public class DigitTextView extends androidx.appcompat.widget.AppCompatTextView {
    public static final String Tag = DigitTextView.class.getSimpleName();

    float baseLineYScale = 0;
    float baseLineXScale = 0;

    private LinearGradient mLinearGradient;

    private boolean isLinearGradientAble = false;

    private ShadowTypeEnum shadowType = ShadowTypeEnum.NOSETUP;//0:不设置，1：阴影，2:浮雕

    private int[] linearGradientColors;

    private CharSequence preString = "";

    private boolean secondSubscript = false;

    private boolean charBackgroundBorder = false;

    private boolean charBackgroundBorderWithDoubble=false;

    private int charBackgroundBorderColor = Color.BLACK;

    private int textWidth = 0;
    private int textBodyHight;
    private int textFontHight;
    private int baseCharWidth;
    private int flagCharwidth;
    private int subscriptTextHight;
    private int subscriptCharWidth;
    private int textLength;
    private int textSubscriptSpan;

    private int fontScale=80;

    private int fontBaseLineHeightChange;

    private HashMap<Integer, AbstractCharAnimator> charAnimatorHashMap = new HashMap<>();

    private CharAnimatorEnum currentCharAnimatorType = CharAnimatorEnum.Marquee3D_Up;

    AbstractCharAnimator charAnimator;

    float startX, startY;

    Paint smallCharPaint = null;

    Paint mTextPaint;

    String charStr;

    CharSequence textToDraw;

    int padding = 2;

    int paddingScale=20;

    int charWidth;

    Paint mDividerPaint, mBackgroundPaint;

    boolean layoutReflushAble=true;

    public DigitTextView(Context context) {
        super(context);
        init();
    }

    public DigitTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DigitTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mDividerPaint = new Paint();
        mDividerPaint.setAntiAlias(true);
        mDividerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mDividerPaint.setColor(Color.WHITE);
        mDividerPaint.setStrokeWidth(10);

        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
    }

    public void setBaseLineDown(float baseLineDown) {
        this.baseLineYScale =baseLineDown;
        layoutReflushAble=true;
    }

    public void setBaseLineY(float baseLineDown) {
        this.baseLineXScale =baseLineDown;
        layoutReflushAble=true;
    }

    float[] pos = {0.1f, 0.3f, 0.5f, 0.7f, 0.9f};

    boolean isLinearGradientPosAble = false;

    boolean isReflectedAble = false;

    boolean linearGradientReCreate = false;


    public void setLinearGradientAble(boolean able) {
        isLinearGradientAble = able;
        linearGradientReCreate = true;
    }

    public void setLinearGradient(int[] colors) {
        this.linearGradientColors = colors;
        linearGradientReCreate = true;
    }

    private void linearGradientReflush() {
        mLinearGradient = null;
        if (isLinearGradientAble && linearGradientColors != null) {
            mLinearGradient = new LinearGradient(0, 0, getWidth(), getHeight(), linearGradientColors, pos, Shader.TileMode.CLAMP);
        }
        getPaint().setShader(mLinearGradient);
        linearGradientReCreate = false;
    }


    public void setShadowType(ShadowTypeEnum shadowType) {
        this.shadowType = shadowType;
        switch (shadowType) {
            case SHADOW://阴影效果
                setShadowLayer(4, 10, 10, Color.BLACK);
                break;
            case RELIEF:
                //setShadowLayer(1, 0.5f, 0.5f, Color.argb(200,204,204,204));//浮雕效果
                setShadowLayer(12, 0, 1, Color.parseColor("#CC2b88f6"));//边缘模糊
                break;
            default:
                setShadowLayer(0, 0, 0, getPaint().getColor());
        }
    }

    public ShadowTypeEnum getShadowType() {
        return shadowType;
    }

    public boolean isLinearGradientAble() {
        return isLinearGradientAble;
    }

    public boolean isLinearGradientPosAble() {
        return isLinearGradientPosAble;
    }

    public int[] getLinearGradientColors() {
        return linearGradientColors;
    }

    public void setLinearGradientPosAble(boolean linearGradientPosAble) {
        isLinearGradientPosAble = linearGradientPosAble;
    }

    public void setLinearGradientColors(int[] linearGradientColors) {
        this.linearGradientColors = linearGradientColors;
    }

    public CharAnimatorEnum getCurrentCharAnimatorType() {
        return currentCharAnimatorType;
    }

    public void setCurrentCharAnimatorType(CharAnimatorEnum currentCharAnimatorType) {
        this.currentCharAnimatorType = currentCharAnimatorType;
    }

    public boolean isReflectedAble() {
        return isReflectedAble;
    }

    public void setReflectedAble(boolean reflectedAble) {
        isReflectedAble = reflectedAble;
    }

    public boolean isSecondSubscript() {
        return secondSubscript;
    }

    public void setSecondSubscript(boolean secondSubscript) {
        this.secondSubscript = secondSubscript;
        layoutReflushAble=true;
    }

    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        layoutReflushAble=true;
    }

    @Override
    public void setTypeface(@Nullable Typeface tf, int style) {
        super.setTypeface(tf, style);
        layoutReflushAble=true;
    }

    private void layoutReflush(CharSequence textToDraw){

        baseCharWidth = getCharWidth("8", mTextPaint);
        padding = baseCharWidth / paddingScale;

        flagCharwidth = getCharWidth(":", mTextPaint);
        textFontHight = (int) getFontHeight(mTextPaint);
        textBodyHight =(int)getFontBodyHeight(mTextPaint);

        subscriptTextHight = textFontHight;
        subscriptCharWidth = baseCharWidth;

        textLength = textToDraw.length();
        textSubscriptSpan = 0;

        if (textLength > 4) {
            textWidth = (baseCharWidth + padding) * 4 + flagCharwidth;
        }

        if (textLength > 7) {
            if (secondSubscript) {
                subscriptCharWidth = getCharWidth("8", smallCharPaint);
                subscriptTextHight =(int) getFontHeight(smallCharPaint);
                fontBaseLineHeightChange=(int)getFontBaseLineHeightChange(mTextPaint,smallCharPaint);
            } else
                textSubscriptSpan = textSubscriptSpan + flagCharwidth;
            textSubscriptSpan = textSubscriptSpan + subscriptCharWidth * 2;
        }

        layoutReflushAble=false;
    }

    private boolean layoutReflushCheck(CharSequence textToDraw){
        if(layoutReflushAble||textToDraw.length()!=preString.length())
            return true;
        return false;
    }


    protected void onDraw(Canvas canvas) {
        //long before=System.currentTimeMillis();
        mTextPaint = getPaint();

        mTextPaint.setColor(getCurrentTextColor());

        textToDraw = getText();

        if (linearGradientReCreate)
            linearGradientReflush();

        if(secondSubscript)
            smallCharPaint = getCustomScaleTextPaint(mTextPaint,  fontScale);

        if(layoutReflushCheck(textToDraw))
            layoutReflush(textToDraw);

        startX = (getWidth() - textWidth - textSubscriptSpan) / 2+getHeight()/2*(-baseLineXScale /100);
        startY = (getHeight() + textBodyHight) / 2+getHeight()/2*(-baseLineYScale /100);

        for (int i = 0; i < textLength; i++) {
            charStr = String.valueOf(textToDraw.charAt(i));
            if (i < 5) {
                if (i == 2) {
                    canvas.drawText(charStr, startX, startY, mTextPaint);
                    startX += flagCharwidth;
                } else {
                    drawCharWithAnaimatorCheck(canvas, i, charStr, startX, startY, mTextPaint);
                    startX = startX + baseCharWidth + padding;
                }
            } else if (i == 5) {
                if (secondSubscript)
                    startX += subscriptCharWidth / 2;
                else {
                    canvas.drawText(charStr, startX, startY , mTextPaint);
                    startX += flagCharwidth;
                }
            } else {
                if (secondSubscript) {
                    if (charBackgroundBorder)
                        if(charBackgroundBorderWithDoubble) {
                            if (i == 6)
                                drawBackground(canvas, startX, startY+fontBaseLineHeightChange-subscriptTextHight+smallCharPaint.getFontMetrics().bottom, subscriptCharWidth*2, subscriptTextHight, mBackgroundPaint);
                        }else
                            drawBackground(canvas, startX, startY+fontBaseLineHeightChange-subscriptTextHight+smallCharPaint.getFontMetrics().bottom, subscriptCharWidth, subscriptTextHight, mBackgroundPaint);
                    drawChar(canvas, charStr, startX, startY+fontBaseLineHeightChange, smallCharPaint);
                    if (charBackgroundBorder)
                        if(charBackgroundBorderWithDoubble) {
                            if (i == 7)
                                drawDivider(canvas, startX-subscriptCharWidth, startY+fontBaseLineHeightChange-subscriptTextHight+smallCharPaint.getFontMetrics().bottom, subscriptCharWidth*2, subscriptTextHight, mDividerPaint);
                        }else
                            drawDivider(canvas, startX, startY+fontBaseLineHeightChange-subscriptTextHight+smallCharPaint.getFontMetrics().bottom, subscriptCharWidth, subscriptTextHight, mDividerPaint);
                } else {
                    drawCharWithAnaimatorCheck(canvas, i, charStr, startX, startY, mTextPaint);
                    startX += padding;
                }
                startX += subscriptCharWidth;
                //timber.log.Timber.d("view hight:"+getHeight()+"\t startY:"+startY+"\t text hight:"+textBodyHight+"small text hight:"+subscriptTextHight);
            }
        }
        preString = textToDraw;
        //timber.log.Timber.d("total draw time:"+(System.currentTimeMillis()-before));
    }

    private void drawBackground(Canvas canvas, float startX, float startY, int width, int height, Paint mBackgroundPaint) {
        mBackgroundPaint.setColor(charBackgroundBorderColor);
        //canvas.drawRoundRect(new RectF(startX, startY, startX + width, startY + height), 10, 10, mBackgroundPaint);
        canvas.drawRoundRect(new RectF(startX, startY, startX + width, startY + height/2-mDividerPaint.getStrokeWidth()/2), 10, 10, mBackgroundPaint);
        canvas.drawRoundRect(new RectF(startX, startY+ height/2+mDividerPaint.getStrokeWidth()/2, startX + width, startY + height), 10, 10, mBackgroundPaint);
    }

    private void drawDivider(Canvas canvas, float startX, float startY, int width, int height, Paint mDividerPaint) {
        canvas.drawLine(startX+mDividerPaint.getStrokeWidth(), startY+height/2, startX+width-mDividerPaint.getStrokeWidth(), startY+height/2, mDividerPaint);
    }

    private void drawChar(Canvas canvas, String c, float startX, float startY, Paint mTextPaint) {
        if (isReflectedAble)
            drawCharReflected(canvas, c, startX, startY, mTextPaint);
        canvas.drawText(c, startX, startY, mTextPaint);
    }

    private void drawCharWithAnaimatorCheck(Canvas canvas, int i, String charStr, float startX, float startY, Paint mTextPaint) {
        charWidth = getCharWidth(charStr, mTextPaint);
        //timber.log.Timber.d("view width:"+getWidth()+"\t heitht:"+getHeight()+"\t startx:"+startX+"\t startY:"+startY+"\t baseCharWidth:"+baseCharWidth+"\t text hight:"+textBodyHight+"\t text font hight:"+textFontHight);
        if (charBackgroundBorder)
            if(charBackgroundBorderWithDoubble) {
                if (i == 0 || i == 3 || i == 6)
                    drawBackground(canvas, startX, startY - textFontHight + mTextPaint.getFontMetrics().bottom, baseCharWidth*2+padding, textFontHight, mBackgroundPaint);
            }else
                drawBackground(canvas, startX, startY-textFontHight+mTextPaint.getFontMetrics().bottom, baseCharWidth, textFontHight, mBackgroundPaint);
        if (currentCharAnimatorType != CharAnimatorEnum.NOSETUP) {
            charAnimator = charAnimatorHashMap.get(i);
            if (charAnimator != null && !charAnimator.isCharAnimatorRuning()) {
                charAnimatorHashMap.remove(i);
                charAnimator = null;
            }
            if (charAnimator == null && preString != null && preString.length() == textToDraw.length() && textToDraw.charAt(i) != preString.charAt(i)) {
                charAnimator = createCharAnimator(String.valueOf(preString.charAt(i)), charStr, currentCharAnimatorType,i>5);
                charAnimatorHashMap.put(i, charAnimator);
            }
            if (charAnimator != null) {
                charAnimator.drawCharAnimator(canvas, startX, startY , mTextPaint);
                //timber.log.Timber.d("charAnimator index i:"+i+"\tanimator percent:");
                if (charBackgroundBorder)
                    if(charBackgroundBorderWithDoubble) {
                        if (i == 1 || i == 4 || i == 7)
                            drawDivider(canvas, startX-baseCharWidth-padding, startY - textFontHight + mTextPaint.getFontMetrics().bottom, baseCharWidth*2+padding, textFontHight, mDividerPaint);
                    }else
                        drawDivider(canvas, startX, startY  - textFontHight+mTextPaint.getFontMetrics().bottom , baseCharWidth, textFontHight, mDividerPaint);
                invalidate();
                return;
            }
        }
        drawChar(canvas, charStr, startX + (baseCharWidth - charWidth) / 2, startY , mTextPaint);
        if (charBackgroundBorder)
            if(charBackgroundBorderWithDoubble) {
                if (i == 1 || i == 4 || i == 7)
                    drawDivider(canvas, startX-baseCharWidth-padding, startY - textFontHight + mTextPaint.getFontMetrics().bottom, baseCharWidth*2+padding, textFontHight, mDividerPaint);
            }else
                drawDivider(canvas, startX, startY  - textFontHight+mTextPaint.getFontMetrics().bottom , baseCharWidth, textFontHight, mDividerPaint);
    }

    Paint reflectedPaint;
    Matrix matrix;
    LinearGradient reflectedShader;

    private void drawCharReflected(Canvas canvas, String c, float startX, float startY, Paint mTextPaint) {
        canvas.save();
        reflectedPaint = new Paint(mTextPaint);
        reflectedShader = new LinearGradient(startX,
                -startY, startX,
                -startY - textBodyHight * 6 / 5,
                Color.BLACK, 0x00ffffff, Shader.TileMode.MIRROR);// 创建线性渐变LinearGradient对象
        matrix = new Matrix();
        matrix.preScale(1, -1); // 实现图片的反转
        reflectedPaint.setShader(reflectedShader); // 绘制
        reflectedPaint.setAlpha(50);
        canvas.setMatrix(matrix);
        canvas.drawText(c, startX, -startY, reflectedPaint);
        canvas.restore();
    }

    private  float numalSpeed=0.03f,fastSpeed=0.06f;

    private AbstractCharAnimator createCharAnimator(String preString, String currentString, CharAnimatorEnum type,boolean isSecond) {
        AbstractCharAnimator charAnimator = null;
        float speed=isSecond?fastSpeed:numalSpeed;
        switch (type) {
            case DOWN2UP:
                charAnimator = new Down2UpCharAnimator(preString, currentString,speed);
                break;
            case UP2DOWN:
                charAnimator = new Up2DownCharAnimator(preString, currentString,speed);
                break;
            case Marquee3D_Up:
                charAnimator = new Marquee3DCharAnimator(preString, currentString, Marquee3DCharAnimator.D2U,speed);
                break;
            case Marquee3D_Down:
                charAnimator = new Marquee3DCharAnimator(preString, currentString, Marquee3DCharAnimator.U2D,speed);
                break;
            case TabDigit:
                charAnimator = new TabDigitCharAnimator(preString, currentString, true,true,new TabConfig(charBackgroundBorder,charBackgroundBorderColor,mDividerPaint.getColor(),(int)mDividerPaint.getStrokeWidth(),isSecond?800f:1500f));
                break;
        }
        return charAnimator;
    }

    private static TextPaint getCustomScaleTextPaint(Paint srcPaint, float fontSizeScale) {
        TextPaint paint = new TextPaint(srcPaint);
        paint.setTextSize(srcPaint.getTextSize()*fontSizeScale/100);   //设定字体大小, sp转换为px
        return paint;
    }

    static Random rand = new Random();

    public static int roundColor() {
        int alpha = 200;
        int r = rand.nextInt(255);
        int g = rand.nextInt(255);
        int b = rand.nextInt(255);
        return alpha << 24 | r << 16 | g << 8 | b;
    }

    public static int getCharWidth(String str, Paint paint) {
        return (int) paint.measureText(str);
    }

    public static float getFontHeight(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.bottom - fm.top;
    }

    public static float getFontBaseLineHeightChange(Paint base,Paint point){
        Paint.FontMetrics fmBase = base.getFontMetrics();
        Paint.FontMetrics fm = point.getFontMetrics();
        return fmBase.ascent-fmBase.top-(fm.ascent-fm.top);
    }

    public static float getFontBodyHeight(Paint paint) {
        Paint.FontMetrics fm = paint.getFontMetrics();
        return fm.descent - fm.ascent;
    }

    public boolean isCharBackgroundBorder() {
        return charBackgroundBorder;
    }

    public void setCharBackgroundBorder(boolean charBackgroundBorder) {
        this.charBackgroundBorder = charBackgroundBorder;
    }

    public int getCharBackgroundBorderColor() {
        return charBackgroundBorderColor;
    }

    public void setCharBackgroundBorderColor(int charBackgroundBorderColor) {
        this.charBackgroundBorderColor = charBackgroundBorderColor;
    }

    public  void setBackgroundBorderDividerColor(int color){
        mDividerPaint.setColor(color);
    }

    public  void setBackgroundBorderDividerWidth(int width){
        mDividerPaint.setStrokeWidth(width);
    }


    public int getFontScale() {
        return fontScale;
    }

    public void setFontScale(int fontScale) {
        this.fontScale = fontScale;
        layoutReflushAble=true;
    }


    public void setPaddingScale(int padding) {
        this.paddingScale = 20-padding;
        if(paddingScale<=0)
            paddingScale=1;
        layoutReflushAble=true;
    }

    public boolean isCharBackgroundBorderWithDoubble() {
        return charBackgroundBorderWithDoubble;
    }

    public void setCharBackgroundBorderWithDoubble(boolean charBackgroundBorderWithDoubble) {
        this.charBackgroundBorderWithDoubble = charBackgroundBorderWithDoubble;
    }
}
